﻿Imports System.IO
Imports System.Runtime.InteropServices
Imports MirEvoSvr.Functions

Module HomeMaps

    Private Structure HomeLoc

        Dim STR_MAP As String
        Dim X As Integer
        Dim y As Integer

    End Structure

    Dim lstHomeCoords As New SortedDictionary(Of String, HomeLoc)

    Public Sub LoadHomeLocations()

        Dim stream As System.IO.StreamReader = FileIO.FileSystem.OpenTextFileReader(Globals.sServerPath & "Map\map_start.txt")
        Dim line As String

        Do Until Stream.EndOfStream

            Try

                line = Stream.ReadLine()
                TrimLineTabs(line)

                If Left(line, 1) <> ";" Then

                    Dim mapname As String, x As Integer, y As Integer
                    mapname = ReadNextValue(line)
                    x = ReadNextValue(line)
                    y = ReadNextValue(line)

                    Dim record As HomeLoc
                    record.STR_MAP = mapname
                    record.X = x
                    record.y = y

                    lstHomeCoords.Add(mapname, record)

                End If

            Catch ex As Exception

            End Try

        Loop

    End Sub

    Public Sub SetHomeMap(ByRef conn As Connection)

        Dim map As MirMap = GetMapFromConn(conn)
        If IsHomeMap(map.file) Then

            Dim sSql As String = "UPDATE TBL_CHARACTER SET STR_HOMEMAP='" & map.file & "' WHERE ID_CHARACTER=" & conn.CharacterID & ";"
            DB.GameDB.ExecuteNonQuery(sSql)

        End If

    End Sub

    Public Function IsHomeMap(file As String) As Boolean

        Return lstHomeCoords.ContainsKey(file)

    End Function

    Public Sub DoTownTeleport(ByRef conn As Connection)

        Dim sSql As String = "SELECT STR_HOMEMAP FROM TBL_CHARACTER WHERE ID_CHARACTER=" & conn.CharacterID & ";"
        Dim row As Collection = DB.GameDB.ExecuteQueryFirstRecord(sSql)

        If Not row Is Nothing Then
            If row.Count > 0 Then

                Try

                    Dim home As HomeLoc = lstHomeCoords.Item(row("STR_HOMEMAP"))
                    Dim info As Protocol.PositionInfo
                    info.STR_MAP = home.STR_MAP
                    info.INT2_X = home.X
                    info.INT2_Y = home.y
                    TeleportPlayer(conn, info)

                Catch
                End Try

            End If
        End If

    End Sub

End Module

Module MapLinks

    Private Structure MapLink
        Dim ToMap As String
        Dim ToServer As String
        Dim FromCoordX As Integer
        Dim FromCoordY As Integer
        Dim ToCoordX As Integer
        Dim ToCoordY As Integer
    End Structure

    Private lstLinks As SortedDictionary(Of String, MapLink)
    Public lstForeignMapDirectory As SortedDictionary(Of String, String) = New SortedDictionary(Of String, String)

    Public Sub AddMapToDirectoy(map As String, server As String)

        lstForeignMapDirectory.Add(map, server)

    End Sub

    Public Function LoadMapLinks() As Integer

        Dim stream As System.IO.StreamReader = FileIO.FileSystem.OpenTextFileReader(Globals.sServerPath & "Map\map_links.txt")
        Dim line As Integer
        Dim str As String
        Dim ML As MapLink
        Dim K As String
        Dim count As Integer, failcount As Integer
        Dim skipcount As Integer

        Output.AddLine("\B0\F6ML>\F0\B8 Reading map link coords from \Map\map_links.txt")

        lstLinks = New SortedDictionary(Of String, MapLink)

        Do Until stream.EndOfStream

            line += 1
            Try

                str = stream.ReadLine()
                TrimLineTabs(str)

                ML = New MapLink

                K = ReadNextValue(str)

                If K.Substring(0, 1) <> ";" Then

                    If Not IsMapOnServer(K) Then
                        skipcount += 1
                    Else

                        ML.ToMap = ReadNextValue(str)

                        If Not IsMapOnServer(ML.ToMap) And Not lstForeignMapDirectory.ContainsKey(ML.ToMap) Then

                            Output.AddLine("\B0\F6ML>\F0\B8 \F7Error on line " & line & " - to map not found \F0!!!")
                            failcount += 1

                        Else

                            ML.ToServer = lstForeignMapDirectory.Item(ML.ToMap)

                            ML.FromCoordX = ReadNextValue(str)
                            ML.FromCoordY = ReadNextValue(str)
                            ML.ToCoordX = ReadNextValue(str)
                            ML.ToCoordY = ReadNextValue(str)

                            If Not GetMapFromName(K).isPassable(ML.FromCoordX, ML.FromCoordY) Then
                                Output.AddLine("\B0\F6ML>\F0\B8 \F7Error on line " & line & " - from coords not passable \F0!!!")
                                failcount += 1
                            Else


                                If ML.ToServer = "" Then
                                    If Not GetMapFromName(ML.ToMap).isPassable(ML.ToCoordX, ML.ToCoordY) Then
                                        Output.AddLine("\B0\F6ML>\F0\B8 \F7Error on line " & line & " - to coords not passable \F0!!!")
                                        failcount += 1
                                    End If
                                Else
                                    lstLinks.Add(K & "/" & ML.FromCoordX & "/" & ML.FromCoordY, ML)
                                    count += 1
                                End If

                            End If

                        End If
                    End If
                End If

            Catch
                Output.AddLine("\B0\F6ML>\F0\B8 \F7Error in map_links.txt on line " & line & " \F0!!!")
            End Try

        Loop

        Output.AddLine("\B0\F6ML>\F0\B8 \F2" & count & "\F0 map links loaded! (\F7" & skipcount & " skipped " & failcount & " failed\F0)")
        Return failcount

    End Function

    Public Sub DoMapLink(Conn As Connection)

        Dim K As String

        K = GetMapFromConn(Conn).file & "/" & Conn.PositionInfo.INT2_X & "/" & Conn.PositionInfo.INT2_Y

        If lstLinks.ContainsKey(K) Then
            Dim ML As MapLink = lstLinks(K)
            Dim Pos As PositionInfo
            Pos.INT2_X = ML.ToCoordX
            Pos.INT2_Y = ML.ToCoordY
            Pos.STR_MAP = ML.ToMap
            TeleportPlayer(Conn, Pos)
        End If
    End Sub

End Module

Friend Class MirMap

    Public objMapLock As New Object

    Public name As String
    Public Fmmap_Index As UInt16
    Public file As String

    Public isPassable(,) As Boolean
    Public Players As List(Of MirMapPlayer)
    Public FloorItems As FloorItems
    Public Mobs As cMobList
    Public NPCs As NPC.mNPCList
    Public lstTimedMagics As List(Of TimedMagic)

    Public Width, Height As Integer

    Public Enum LightModes
        DAY = 0
        DARK = 1
        DAYNIGHT = 2
    End Enum

    Public Enum PVPModes
        NORMAL = 0
        SAFE = 1
        PVP = 2
    End Enum

    Public AmbientLightMode As LightModes
    Public PVPMode As PVPModes

    Public Function IsVisibleToPlayer(x As Integer, y As Integer) As Boolean

        SyncLock objMapLock
            If Players.Count > 0 Then

                For Each plyr As MirMapPlayer In Players

                    If plyr.conn.PositionInfo.INT2_X > x - Constants.UpdateAreaX And plyr.conn.PositionInfo.INT2_X < x + Constants.UpdateAreaX And plyr.conn.PositionInfo.INT2_Y > y - Constants.UpdateAreaY And plyr.conn.PositionInfo.INT2_Y < y + Constants.UpdateAreaY Then
                        Return True
                    End If

                Next

            End If

            Return False

        End SyncLock

    End Function

End Class


Module mMaps

    <StructLayout(LayoutKind.Explicit, Size:=28)> _
    Public Structure RawSmallTile
        <FieldOffset(0)> <MarshalAs(UnmanagedType.I1)> Dim passable As Byte '2= blocked 3= passable
        <FieldOffset(2)> <MarshalAs(UnmanagedType.I1)> Dim Data1 As Byte
        <FieldOffset(4)> <MarshalAs(UnmanagedType.I1)> Dim Data2 As Byte
        <FieldOffset(6)> <MarshalAs(UnmanagedType.I1)> Dim TopSet As Byte
        <FieldOffset(8)> <MarshalAs(UnmanagedType.I1)> Dim BottomSet As Byte
        <FieldOffset(10)> <MarshalAs(UnmanagedType.I2)> Dim BottomTile As UInt16
        <FieldOffset(14)> <MarshalAs(UnmanagedType.I2)> Dim TopTile As UInt16
        <FieldOffset(18)> <MarshalAs(UnmanagedType.I1)> Dim Data3 As Byte
        <FieldOffset(20)> <MarshalAs(UnmanagedType.I1)> Dim Data4 As Byte
        <FieldOffset(22)> <MarshalAs(UnmanagedType.I1)> Dim Data5 As Byte
        <FieldOffset(24)> <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Dim Light() As Byte
    End Structure

    Public objMapsLock As New Object
    Public mMaps As New List(Of MirMap)

    Public Function LoadMaps() As Integer

        Dim strFile As String, strName As String
        Dim idxFmmap As UInt16
        Dim count As Integer, failcount As Integer

        Output.AddLine("\B0\F7MP>\F0\B8 Loading maps from map_info.txt...")

        If Microsoft.VisualBasic.FileIO.FileSystem.FileExists(Globals.sServerPath & "Map\map_info.txt") Then

            Dim stream As System.IO.StreamReader = FileIO.FileSystem.OpenTextFileReader(Globals.sServerPath & "Map\map_info.txt")
            Dim line As String

            Do Until stream.EndOfStream

                Try

                    line = stream.ReadLine()
                    TrimLineTabs(line)

                    If Left(line, 1) <> ";" Then

                        Dim svr As String
                        svr = ReadNextValue(line)
                        strFile = ReadNextValue(line)

                        If svr = Globals.ServerName Then

                            Try


                                strName = ReadNextValue(line)
                                idxFmmap = ReadNextValue(line)

                                Dim attrib As New Collection
                                Dim tmpattrib As String

                                Do
                                    If line.Length > 0 Then
                                        tmpattrib = ReadNextValue(line)
                                    Else
                                        Exit Do
                                    End If
                                Loop

                                If Not FileIO.FileSystem.FileExists(Globals.sServerPath & "Map\" & strFile & ".map") Then
                                    Output.AddLine("\B0\F7MP>\F0\B8 Map \F7" & strFile & "\F0 not found in maps dir!")
                                    failcount += 1
                                Else
                                    LoadMap(strFile, strName, idxFmmap, attrib)
                                    count += 1
                                    Logger.LoggerWriteLine("SV> " & strFile & " done (#" & count & ")")
                                End If

                            Catch

                            End Try

                        End If

                        MapLinks.AddMapToDirectoy(strFile, svr)

                    End If

                Catch ex As Exception

                End Try

            Loop

        End If

        Output.AddLine("\B0\F5SV>\F0\B8 Loaded \F2" & count & "\F0 maps! (\F7" & failcount & " failed\F0)")

        HomeMaps.LoadHomeLocations()

        Return failcount

    End Function

    Private Sub LoadMap(ByVal filename As String, ByVal mapname As String, ByVal fmmap_index As UInt16, attrib As Collection)

        'open the map file
        Dim mapStream As New FileStream(Globals.sServerPath & "Map\" & filename & ".map", FileMode.Open, FileAccess.Read)
        Dim bBuffer(1) As Byte
        Dim itX As Integer, itY As Integer
        Dim w As Integer, h As Integer

        'Skip header bytes
        mapStream.Position = 22

        'read map dimensions
        mapStream.Read(bBuffer, 0, 2)
        w = ArrayToStruct(bBuffer, GetType(UInt16))
        mapStream.Read(bBuffer, 0, 2)
        h = ArrayToStruct(bBuffer, GetType(UInt16))

        'skip 2 bytes, don't know what these do atm, maybe related to tile sets
        mapStream.Position += 2

        '=== LARGE TILES ===

        ReDim bBuffer(((w \ 2) * (h \ 2) * 3) - 1)
        mapStream.Read(bBuffer, 0, (((w \ 2) * (h \ 2)) * 3))


        ReDim bBuffer(mapStream.Length - mapStream.Position - 1)
        mapStream.Read(bBuffer, 0, (mapStream.Length - mapStream.Position))

        Dim buffPos As Integer

        Dim tmpRawSmallTile As RawSmallTile
        Dim tmp As New MirMap
        tmp.Fmmap_Index = fmmap_index

        ReDim tmp.isPassable(w - 1, h - 1)
        tmp.Width = w : tmp.Height = h

        For itX = 0 To w - 1
            For itY = 0 To h - 1

                tmpRawSmallTile = ArrayToStruct(bBuffer, GetType(RawSmallTile), buffPos)

                '3 = passable
                If tmpRawSmallTile.passable = 3 Then
                    tmp.isPassable(itX, itY) = True
                Else
                    tmp.isPassable(itX, itY) = False
                End If

                buffPos += 14

            Next
        Next

        tmp.name = mapname
        tmp.file = filename
        tmp.Players = New List(Of MirMapPlayer)
        tmp.Mobs = New cMobList(filename, tmp)
        tmp.objMapLock = New Object
        tmp.lstTimedMagics = New List(Of TimedMagic)
        tmp.FloorItems = New FloorItems(filename, w, h)
        tmp.NPCs = New NPC.mNPCList(tmp)

        'Attributes
        If attrib.Contains("DAY") Then
            tmp.AmbientLightMode = MirMap.LightModes.DAY
        ElseIf attrib.Contains("DARK") Then
            tmp.AmbientLightMode = MirMap.LightModes.DARK
        ElseIf attrib.Contains("DAYNIGHT") Then
            tmp.AmbientLightMode = MirMap.LightModes.DAYNIGHT
        End If

        If attrib.Contains("PVP") Then
            tmp.PVPMode = MirMap.PVPModes.PVP
        ElseIf attrib.Contains("SAFE") Then
            tmp.PVPMode = MirMap.PVPModes.SAFE
        ElseIf attrib.Contains("DAYNIGHT") Then
            tmp.AmbientLightMode = MirMap.LightModes.DAYNIGHT
        End If

        tmp.NPCs.Populate(Globals.sServerPath & "NPCDef\" & filename & ".txt")

        SyncLock objMapsLock

            mMaps.Add(tmp)

        End SyncLock

        tmp.Mobs.LoadSpawns()
        'tmp.Mobs.DoSpawn()

        mapStream.Close()

    End Sub

    Public SpawnTicker As Long

    Public Sub DoMapMobOperations(ByVal tc As Long)

        If mMaps.Count = 0 Then Exit Sub

        Dim c As Integer = mMaps.Count - 1
        Dim bDoSpawn As Boolean
        Dim spawnedcount As Long

        If SpawnTicker <= tc Then
            SpawnTicker = tc + iSpawnInterval
            bDoSpawn = True
        End If


        For i As Integer = 0 To c

            SyncLock objMapsLock

                Dim map As MirMap = mMaps(i)

                If bDoSpawn Then spawnedcount += map.Mobs.DoSpawn()

                map.Mobs.DoAI(tc)

                mMaps(i) = map

            End SyncLock

        Next

        If bDoSpawn And spawnedcount > 0 Then

            Output.AddLine("\B0\F5SV>\B8\F0 RESPAWN: \F2\B0" & spawnedcount & "\B8\F0")

        End If

    End Sub

    Public Function GetMapFromConn(ByRef Conn As Connection) As MirMap

        Dim i As Integer
        Dim map As MirMap
        Dim plyr As MirMapPlayer

        Do Until i >= mMaps.Count

            map = mMaps(i)

            SyncLock map.objMapLock
                If map.Players.Count > 0 Then

                    For Each plyr In map.Players
                        If plyr.conn.Index = Conn.Index Then
                            Return map
                        End If
                    Next
                End If

            End SyncLock

            i += 1

        Loop

        Return Nothing

    End Function

    Public Function GetMapFromName(ByVal mapname As String) As MirMap

        Dim i As Integer
        Dim map As MirMap

        Do Until i >= mMaps.Count

            map = mMaps(i)

            If map.file = mapname Then
                Return mMaps(i)
            End If

            i += 1

        Loop

        Return Nothing

    End Function

    Public Function IsMapOnServer(mapname As String) As Boolean

        Dim i As Integer
        Dim map As MirMap

        Do Until i >= mMaps.Count

            map = mMaps(i)

            If map.file = mapname Then
                Return True
            End If

            i += 1

        Loop

        Return False


    End Function

    Public Sub RemovePlayer(ByRef Conn As Connection, ByRef map As MirMap)

        If map Is Nothing Then Exit Sub

        Dim plyr As MirMapPlayer = Nothing

        SyncLock map.objMapLock

            For Each plyr In map.Players
                If plyr.conn Is Conn Then

                    map.Players.Remove(plyr)

                    UpdateLocalPlayers(CharacterUpdateType.Remove, map, Conn)
                    Output.AddLine("\B0\F5SV>\B8\F0 ~~~ Character #" & Conn.CharacterID & " [\B9\F4Acc: " & Conn.Username & "\B8\F0] removed at [Map: \F5" & map.file & "\F0]@" & plyr.conn.PositionInfo.INT2_X & ":" & plyr.conn.PositionInfo.INT2_Y)

                    Exit For
                End If
            Next

        End SyncLock

    End Sub

    Public Sub AddPlayer(ByRef Conn As Connection, ByRef map As MirMap, ByVal x As Integer, ByVal y As Integer, ByVal anim As HUMAnimation, ByVal dir As Direction)

        Dim tmpplyr As MirMapPlayer

        tmpplyr.conn = Conn
        tmpplyr.conn.PositionInfo.INT2_X = x
        tmpplyr.conn.PositionInfo.INT2_Y = y
        tmpplyr.conn.PositionInfo.BYTE_ANIM = anim
        tmpplyr.conn.PositionInfo.BYTE_DIRECTION = dir

        SyncLock map.objMapLock

            map.Players.Add(tmpplyr)

        End SyncLock

        Output.AddLine("\B0\F5SV>\B8\F0 ~~~ Character #" & Conn.CharacterID & " [\B9\F4Acc: " & Conn.Username & "\B8\F0] spawned at [Map: \F5" & map.file & "\F0]@" & tmpplyr.conn.PositionInfo.INT2_X & ":" & tmpplyr.conn.PositionInfo.INT2_Y)

        UpdatePlayer(Conn, map, x, y)
        map.FloorItems.UpdatePlayer(Conn, Conn.PositionInfo.INT2_X, Conn.PositionInfo.INT2_Y)

        UpdateLocalPlayers(CharacterUpdateType.Add, map, Conn)
        TeleportMagic(Conn)

        SetHomeMap(Conn)

    End Sub


    Public Function GetPlayerAtPos(ByRef map As MirMap, ByVal x As Integer, ByVal y As Integer) As MirMapPlayer

        Dim i As Integer
        Dim plyr As MirMapPlayer

        SyncLock map.objMapLock

            Do Until i >= map.Players.Count

                plyr = map.Players(i)

                If plyr.conn.IsAlive And plyr.conn.PositionInfo.INT2_X = x And plyr.conn.PositionInfo.INT2_Y = y Then
                    Return plyr
                End If

                i += 1

            Loop

        End SyncLock


        Return Nothing

    End Function

    Public Function IsPassable(ByVal map As MirMap, ByVal x As Integer, ByVal y As Integer) As Boolean

        If x >= map.Width Or y >= map.Height Then Return False

        If map.isPassable(x, y) Then
            If Not GetPlayerAtPos(map, x, y).conn Is Nothing Then
                Return False
            End If
            If map.Mobs.GetMobAtPos(x, y).ServerMobID <> 0 Then
                Return False
            End If
            Return True
        Else
            Return False
        End If
    End Function


    Public Sub MovePlayer(ByRef Conn As Connection, ByRef map As MirMap, ByVal info As PlayerPositionInfo)

        Dim mov As Point = CalcNextStep(info.INT2_X, info.INT2_Y, info.BYTE_DIRECTION)

        If info.BYTE_ANIM = HUMAnimation.Attack Then

            'If conn.AttackMode <> AttackModes.Peace Then
            Dim plyrinfo As MirMapPlayer = GetPlayerAtPos(map, mov.X, mov.Y)
            If Not plyrinfo.conn Is Nothing Then

                PlayerHitPlayer(Conn, plyrinfo.conn)

            Else


                Dim mobinfo As MirMapMob = map.Mobs.GetMobAtPos(mov.X, mov.Y)
                If mobinfo.ServerMobID <> 0 And mobinfo.IsAlive Then
                    PlayerHitMob(Conn, mobinfo)
                    mobinfo.map.Mobs.SetMob(mobinfo)

                End If
            End If
        End If

        Dim tmpplyr As MirMapPlayer

        SyncLock map.objMapLock

            For Each tmpplyr In map.Players
                If tmpplyr.conn Is Conn Then
                    If Not IsPassable(map, info.INT2_X, info.INT2_Y) And Not (info.INT2_X = tmpplyr.conn.PositionInfo.INT2_X And info.INT2_Y = tmpplyr.conn.PositionInfo.INT2_Y) Then '((info.BYTE_ANIM = HUMAnimation.Walking Or info.BYTE_ANIM = HUMAnimation.Running) And Not IsPassable(conn, tmpmap, mov.X, mov.Y)) Then

                        Dim reply As Packet
                        reply.Username = Conn.Username
                        reply.PacketType = GamePacketType.PlayerMove
                        reply.PacketOpCode = PacketOpCode.Notify
                        Dim posinfo As PositionInfo
                        posinfo.STR_MAP = map.file
                        posinfo.INT2_X = tmpplyr.conn.PositionInfo.INT2_X
                        posinfo.INT2_Y = tmpplyr.conn.PositionInfo.INT2_Y

                        Socket.SockServer.SendData(Protocol.MakePacket(reply, posinfo, Conn.Crypto), Conn.Index)

                    Else

                        map.Players.Remove(tmpplyr)
                        tmpplyr.conn.PositionInfo = info
                        map.Players.Add(tmpplyr)

                        UpdateLocalPlayers(CharacterUpdateType.Move, map, Conn)

                        'See if there is a map link on this square
                        DoMapLink(Conn)

                    End If

                    Exit Sub
                End If
            Next

        End SyncLock

    End Sub

    Public Sub SwitchServer(ByRef Conn As Connection, ByRef map As String, ByVal info As PositionInfo)

        'check ForeignMap>Server Directory to make sure a cluster server is running the map
        If MapLinks.lstForeignMapDirectory.ContainsKey(info.STR_MAP) Then

            'delete any excess entries
            Dim sSQL As String = "DELETE FROM TBL_XSERVERCONN WHERE STR_IPADDRESS='" & Conn.IP & "';"
            DB.GameDB.ExecuteNonQuery(sSQL)
            'save destination info to db
            sSQL = "INSERT INTO TBL_XSERVERCONN (STR_IPADDRESS, STR_UNAME, STR_DESTSERVER, ID_LOGIN, ID_CHARACTER, STR_MAP, INT_X, INT_Y) VALUES ('" & Conn.IP & "', '" & Conn.Username & "', '" & MapLinks.lstForeignMapDirectory(info.STR_MAP) & "', " & Conn.LoginID & ", " & Conn.CharacterID & ", '" & info.STR_MAP & "', " & info.INT2_X & ", " & info.INT2_Y & ");"
            DB.GameDB.ExecuteNonQuery(sSQL)

            'send switchserver packet
            Dim pkt As Packet
            pkt.PacketType = GamePacketType.SwitchServer
            pkt.PacketOpCode = PacketOpCode.Notify
            pkt.Username = Conn.Username
            
            'get the hostname:port combo
            Dim svrinfo As SwitchServer = Globals.GetServerSwitcher(MapLinks.lstForeignMapDirectory(info.STR_MAP))

            Socket.SockServer.SendData(Protocol.MakePacket(pkt, svrinfo, Conn.Crypto), Conn.Index)

            Packets.UpdateUserList_Offline(Conn.CharacterID)

            'remove player from source
            Dim frommap As MirMap = GetMapFromName(map)
            If Not frommap Is Nothing Then
                RemovePlayer(Conn, frommap)
            End If

        End If

    End Sub

    Public Sub RandomTeleport(ByRef conn As Connection)

        Dim map As MirMap = GetMapFromConn(conn)
        Dim info As PositionInfo

        Do

            info.INT2_X = Int(Rnd() * map.Width)
            info.INT2_Y = Int(Rnd() * map.Height)
            info.STR_MAP = map.file

        Loop Until map.isPassable(info.INT2_X, info.INT2_Y)

        TeleportPlayer(conn, info)

    End Sub

    Public Function TeleportPlayer(ByRef Conn As Connection, ByVal info As PositionInfo)

        Dim frommap As MirMap = GetMapFromConn(Conn)
        Dim map As String

        If (frommap Is Nothing And Not IsMapOnServer(info.STR_MAP)) Then
            map = ""
        ElseIf Not frommap Is Nothing Then
            map = frommap.file
        Else
            Return False
        End If

        'create structure to hold the map we are teleporting to, and get that map
        Dim tomap As MirMap = GetMapFromName(info.STR_MAP)

        'chek if map is on this server
        If Not IsMapOnServer(info.STR_MAP) Then
            'map NOT on server

            SwitchServer(Conn, map, info)
            Return True

        Else

            SyncLock frommap.objMapLock

                'itterate through all player on the FROM map
                If frommap.Players.Count > 0 Then
                    For Each tmpplyr As MirMapPlayer In frommap.Players
                        'check if this player is the one we want to teleport
                        If tmpplyr.conn Is Conn Then
                            'check if the teleport destination is clear
                            'TODO: if destination isn't clear, pick a clear one nearby
                            If IsPassable(tomap, info.INT2_X, info.INT2_Y) Then

                                'remove player from the FROM map
                                RemovePlayer(Conn, frommap)
                                'add player to the TO (destination) map
                                AddPlayer(Conn, tomap, info.INT2_X, info.INT2_Y, Conn.PositionInfo.BYTE_ANIM, Conn.PositionInfo.BYTE_DIRECTION)

                                'update the player that is teleporting about their new map & coordinates
                                Dim pkt As Packet
                                pkt.PacketType = GamePacketType.LoadNewMap
                                pkt.PacketOpCode = PacketOpCode.Notify
                                pkt.Username = Conn.Username

                                Dim struct As LoadMapInfo
                                struct.Posinfo = info
                                struct.STR_MAPNAME = GetMapFromName(info.STR_MAP).name
                                struct.Fmmap_Index = GetMapFromName(info.STR_MAP).Fmmap_Index

                                Socket.SockServer.SendData(Protocol.MakePacket(pkt, struct, Conn.Crypto), Conn.Index)

                                UpdatePlayer(Conn, tomap, info.INT2_X, info.INT2_Y)

                                'sleep for 500ms to give time for game to sync up
                                Threading.Thread.Sleep(100)

                                'tell the client to enter the game
                                pkt.PacketType = GamePacketType.EnterGame
                                pkt.PacketOpCode = PacketOpCode.Notify
                                Socket.SockServer.SendData(Protocol.MakePacket(pkt, , Conn.Crypto), Conn.Index)

                                'teleport was a success
                                Return True

                            End If

                        End If
                    Next

                End If

            End SyncLock
            'could not find player in FROM map, or (more likely) the destination coordinates were blocked
            Return False

        End If

    End Function

End Module
